<?php namespace Hdphp\Weixin;

class Weixin{
	
	use Error,Button,Event,Receive,Reply;
  	
  	//TOKEN定义
    private $token='weixin';

    //API 根地址
    const API_URL = 'https://api.weixin.qq.com';

    private $appID='wxcf444c17dbd095b9';

    private $appsecret='6fd451c0cff05b6c39c51dd7f7bff562';

    //微信 服务器发来的数据
	private $receive;

    #-------------------请求----------------
    //请求文本消息
	CONST MSG_TYPE_TEXT = 'text';

	//请求图片消息
	CONST MSG_TYPE_IMAGE = 'image';

	//请求语音消息
	CONST MSG_TYPE_VOICE = 'voice';

	//请求地理位置消息
	CONST MSG_TYPE_LOCATION = 'location';

	//请求链接消息
	CONST MSG_TYPE_LINK = 'link';

	//请求小视频消息
	CONST MSG_TYPE_SMALL_VIDEO = 'shortvideo';

	//请求视频消息
	CONST MSG_TYPE_VIDEO = 'video';

	#-------------------事件----------------
	//关注事件
	CONST EVENT_TYPE_SUBSCRIBE = 'subscribe';

	//取消关注事件
	CONST EVENT_TYPE_UNSUBSCRIBE = 'unsubscribe';

	//未关注用户扫描二维码事件
	CONST EVENT_TYPE_UNSUBSCRIBE_SCAN = 'subscribe';

	//关注用户扫描二维码事件
	CONST EVENT_TYPE_SUBSCRIBE_SCAN = 'SCAN';

	//上报地理位置事件
	CONST EVENT_TYPE_LOCATION = 'LOCATION';

	//点击菜单拉取消息时的事件
	CONST EVENT_TYPE_CLICK = 'CLICK';

	//点击菜单跳转链接时的事件
	CONST EVENT_TYPE_VIEW = 'VIEW';

	#-------------------回复内容----------------
	//回复文本
	CONST REPLY_TYPE_TEXT = 'text';

	//回复图文
	CONST REPLY_TYPE_IMAGE = 'image';

	//回复语音
   	CONST REPLY_TYPE_VOICE = 'voice';

   	//回复视频
   	CONST REPLY_TYPE_VIDEO = 'video';

   	//音乐消息
   	CONST REPLY_TYPE_MUSIC = 'music';

   	//图文信息
   	CONST REPLY_TYPE_NEWS = 'news';

    /**
     access_token是公众号的全局唯一票据，公众号调用各接口时都需使用access_token。开发者需要进行妥善保存。access_token的存储至少要保留512个字符空间。access_token的有效期目前为2个小时，需定时刷新，重复获取将导致上次获取的access_token失效，每天可获取2000次
     */
    private $access_token;

    //服务器返回的 access_token 过期时间，一般2小时
    private $expires_in;

    public function __construct()
    {
        //处理 微信服务器 发来的数据
        $this->parsetReceivePostData();
    }

    /**
     * 微信接口整合验证
     * @return [type] [description]
     */
    public function valid()
    {
        $echoStr = $_GET["echostr"];
        //验证微信请求
        if($this->checkSignature())
        {
        	echo $echoStr;
        	exit;
        }
    }

    //获得微信服务器发来的数据
    public function getReceive()
    {
        return $this->receive;
    }

    //设置appID
    public function appID($appID)
    {
        $this->appID = $appID;
        return $this;
    }

    //设置appsecret
    public function appsecret($appsecret)
    {
        $this->appsecret = $appsecret;
        return $this;
    }

    /**
     * 验证微信请求
     * @return [type] [description]
     */
    private function checkSignature()
    {
        $signature = $_GET["signature"];
        $timestamp = $_GET["timestamp"];
        $nonce = $_GET["nonce"];
        $token = $this->token;
        $tmpArr = array($token, $timestamp, $nonce);
        sort($tmpArr, SORT_STRING);
        $tmpStr = implode( $tmpArr );
        $tmpStr = sha1( $tmpStr );

        if( $tmpStr == $signature )
        {
           return true;
        }
        else
        {
         return false;
        }
   }

   //设置 TOKEN
   public function setToken($token)
   {
        $this->token = $token;
        return $this;
   }
    /**
     * 获取accessToken
     * 只有获取到TOKEN才可以使用各种API接口
     * @return [type] [description]
     */
    public function getAccessToken()
    {
        $url = self::API_URL.'/cgi-bin/token?grant_type=client_credential&appid='.$this->appID.'&secret='.$this->appsecret;
        $cacheKey = md5($url);
        if($token = Cache::dir('storage/weixin')->get($cacheKey))
        {
            return $token;
        }
        $data = $this->curl($url);

        $json = json_decode($data,true);

        if(array_key_exists('errcode', $json) && $json['errcode'] !=0)
        {
            //获取失败
            return false;
        }
        else
        {
            $this->access_token=$json['access_token'];
            $this->expires_in = (int)$json['expires_in'];
            //应该保存缓存。。。
            Cache::dir('storage/weixin')->set($cacheKey,$this->access_token,$this->expires_in);
            return $this->access_token;
        }
    }

    //解析接收的微信服务器发来的POST XML 数据
    public function parsetReceivePostData()
    {
        $post = file_get_contents('php://input');
        // $post = $GLOBALS['HTTP_RAW_POST_DATA'];
        $data = simplexml_load_string($post,'SimpleXMLElement',LIBXML_NOCDATA);

        if($data !==false)
        {
            $this->receive = $data;
        }

        return $data;
    }

    //CURL请求
    private function curl($url)
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);

        if(!curl_exec($ch))
        {
            $data='';
        }
        else
        {
            $data = curl_multi_getcontent($ch);
        }
        curl_close($ch);
        return $data;
    }

    //curl 提交数据
    private function curlPost($url,$jsonData)
    {
        $ch = curl_init();  
        curl_setopt($ch, CURLOPT_URL, $url);  
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);  
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);  
        curl_setopt($ch, CURLOPT_POST, 1);  
        curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonData);  
        curl_setopt($ch, CURLOPT_TIMEOUT, 30);  
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);  
        $data = curl_exec($ch);
        curl_close($ch);  
        return $data;
    }


    //addby 梁子亮 2015年7月31日09:31:54
    /**
     * 上传临时资源
     * @param  string $filename 媒体资源本地路径
     * @param  string $type     媒体资源类型，具体请参考微信开发手册
     */
    public function mediaUpload($filename, $type,$token){
        $param = array(
            'access_token' => $token,
            'type'         => $type
        );
        // var_dump($param);die;

        $filename = realpath($filename);
        if(!$filename) throw new \Exception('资源路径错误！');
        
        $file = array('media' => "@{$filename}");
        $url  = "http://file.api.weixin.qq.com/cgi-bin/media/upload";
        $data = self::http($url, $param, $file, 'POST');

        return json_decode($data, true);
    }


    

    /**
     * 删除永久素材(认证后的订阅号可用)
     * @param string $media_id 媒体文件id
     * @return boolean
     */
    public function delForeverMedia($media_id){
        // if (!$this->access_token && !$this->checkAuth()) return false;
        $data = array('media_id' => $media_id);
        $result = $this->http_post('https://api.weixin.qq.com/cgi-bin/material/del_material?'.'access_token='.$this->token,self::json_encode($data));
        var_dump($result);die;
        if ($result)
        {
            $json = json_decode($result,true);
            if (!$json || !empty($json['errcode'])) {
                $this->errCode = $json['errcode'];
                $this->errMsg = $json['errmsg'];
                return false;
            }
            return true;
        }
        return false;
    }


    /**
     * 获取媒体资源下载地址
     * 注意：视频资源不允许下载
     * @param  string $media_id 媒体资源id
     * @return string           媒体资源下载地址
     */
    public function mediaGet($media_id,$token){
        $param = array(
            'access_token' => $token,
            'media_id'     => $media_id
        );

        $url = "http://file.api.weixin.qq.com/cgi-bin/media/get?";
        return $url . http_build_query($param);
    }

    /**
     * 发送HTTP请求方法，目前只支持CURL发送请求
     * @param  string $url    请求URL
     * @param  array  $param  GET参数数组
     * @param  array  $data   POST的数据，GET请求时该参数无效
     * @param  string $method 请求方法GET/POST
     * @return array          响应数据
     */
    protected static function http($url, $param, $data = '', $method = 'GET'){
        $opts = array(
            CURLOPT_TIMEOUT        => 30,
            CURLOPT_RETURNTRANSFER => 1,
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_SSL_VERIFYHOST => false,
        );

        /* 根据请求类型设置特定参数 */
        $opts[CURLOPT_URL] = $url . '?' . http_build_query($param);

        if(strtoupper($method) == 'POST'){
            $opts[CURLOPT_POST] = 1;
            $opts[CURLOPT_POSTFIELDS] = $data;
            
            if(is_string($data)){ //发送JSON数据
                $opts[CURLOPT_HTTPHEADER] = array(
                    'Content-Type: application/json; charset=utf-8',  
                    'Content-Length: ' . strlen($data),
                );
            }
        }

        /* 初始化并执行curl请求 */
        $ch = curl_init();
        curl_setopt_array($ch, $opts);
        $data  = curl_exec($ch);
        $error = curl_error($ch);
        curl_close($ch);

        //发生错误，抛出异常
        if($error) throw new \Exception('请求发生错误：' . $error);

        return  $data;
    }


    /**
     * 微信api不支持中文转义的json结构
     * @param array $arr
     */
    static function json_encode($arr) {
        $parts = array ();
        $is_list = false;
        //Find out if the given array is a numerical array
        $keys = array_keys ( $arr );
        $max_length = count ( $arr ) - 1;
        if (($keys [0] === 0) && ($keys [$max_length] === $max_length )) { //See if the first key is 0 and last key is length - 1
            $is_list = true;
            for($i = 0; $i < count ( $keys ); $i ++) { //See if each key correspondes to its position
                if ($i != $keys [$i]) { //A key fails at position check.
                    $is_list = false; //It is an associative array.
                    break;
                }
            }
        }
        foreach ( $arr as $key => $value ) {
            if (is_array ( $value )) { //Custom handling for arrays
                if ($is_list)
                    $parts [] = self::json_encode ( $value ); /* :RECURSION: */
                else
                    $parts [] = '"' . $key . '":' . self::json_encode ( $value ); /* :RECURSION: */
            } else {
                $str = '';
                if (! $is_list)
                    $str = '"' . $key . '":';
                //Custom handling for multiple data types
                if (!is_string ( $value ) && is_numeric ( $value ) && $value<2000000000)
                    $str .= $value; //Numbers
                elseif ($value === false)
                $str .= 'false'; //The booleans
                elseif ($value === true)
                $str .= 'true';
                else
                    $str .= '"' . addslashes ( $value ) . '"'; //All other things
                // :TODO: Is there any more datatype we should be in the lookout for? (Object?)
                $parts [] = $str;
            }
        }
        $json = implode ( ',', $parts );
        if ($is_list)
            return '[' . $json . ']'; //Return numerical JSON
        return '{' . $json . '}'; //Return associative JSON
    }


    /**
     * GET 请求
     * @param string $url
     */
    private function http_get($url){
        $oCurl = curl_init();
        if(stripos($url,"https://")!==FALSE){
            curl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE);
            curl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, FALSE);
            curl_setopt($oCurl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1
        }
        curl_setopt($oCurl, CURLOPT_URL, $url);
        curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1 );
        $sContent = curl_exec($oCurl);
        $aStatus = curl_getinfo($oCurl);
        curl_close($oCurl);
        if(intval($aStatus["http_code"])==200){
            return $sContent;
        }else{
            return false;
        }
    }

    /**
     * POST 请求
     * @param string $url
     * @param array $param
     * @param boolean $post_file 是否文件上传
     * @return string content
     */
    private function http_post($url,$param,$post_file=false){
        $oCurl = curl_init();
        if(stripos($url,"https://")!==FALSE){
            curl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE);
            curl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, false);
            curl_setopt($oCurl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1
        }
        if (is_string($param) || $post_file) {
            $strPOST = $param;
        } else {
            $aPOST = array();
            foreach($param as $key=>$val){
                $aPOST[] = $key."=".urlencode($val);
            }
            $strPOST =  join("&", $aPOST);
        }
        curl_setopt($oCurl, CURLOPT_URL, $url);
        curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1 );
        curl_setopt($oCurl, CURLOPT_POST,true);
        curl_setopt($oCurl, CURLOPT_POSTFIELDS,$strPOST);
        $sContent = curl_exec($oCurl);
        $aStatus = curl_getinfo($oCurl);
        curl_close($oCurl);
        if(intval($aStatus["http_code"])==200){
            return $sContent;
        }else{
            return false;
        }
    }

    //获取永久素材列表 -- 梁子亮
    //$data = array('type'=>'image','offset'=>0,'count'=>8);
    public function getForverList($data=null)
    {
        $url = 'https://api.weixin.qq.com/cgi-bin/material/batchget_material?access_token='.$this->getAccessToken();
        // $url = self::API_URL.'/cgi-bin/menu/create?access_token='.$this->getAccessToken();
        $newdata = json_encode($data);
        $content = $this->curlPost($url,$newdata);
        var_dump($content);die;

        $result = json_decode($content,true);

        return $this->get($result);
        // var_dump($result);die;
    }

    /**
     * 上传永久图像资源 -- 梁子亮 2015年7月31日01:56:55
     * @param  string $filename 媒体资源本地路径$filename = 'Upload/14981438412107.jpg';
     * @param  string $type     媒体资源类型，具体请参考微信开发手册
     */
    public function uploadForverImg($filename, $type){
        $param = array(
            'access_token' => $this->getAccessToken(),
            'type'         => $type
        );

        $filename = realpath($filename);
        if(!$filename) throw new \Exception('资源路径错误！');
        
        $file = array('media' => "@{$filename}");
        // $url = "http://file.api.weixin.qq.com/cgi-bin/media/upload";
        $url = "https://api.weixin.qq.com/cgi-bin/material/add_material";
        $data = self::http($url, $param, $file, 'POST');

        return json_decode($data, true);
    }

    /**
     * 上传永久图像资源 -- 梁子亮 2015年8月1日17:28:21,暂不可用，不知为何
     * @param  string $filename 媒体资源本地路径$filename = 'Upload/14981438412107.jpg';
     * @param  string $type     媒体资源类型，具体请参考微信开发手册
     */
    public function uploadForverImg2($filename, $type){
        $filename = realpath($filename);
        if(!$filename) throw new \Exception('资源路径错误！');
        $param = array(
            'media' => "@{$filename}"
        );
        
        $url = "https://api.weixin.qq.com/cgi-bin/material/add_material?access_token=".$this->getAccessToken() . "&type=" . $type;
        $data = self::curlPost($url,self::json_encode($param));
        var_dump($data);die;
        return json_decode($data, true);
    }

    /**
     * 获取素材总数 -- 梁子亮 2015年8月1日17:28:21
     * @param  string $filename 媒体资源本地路径$filename = 'Upload/14981438412107.jpg';
     * @param  string $type     媒体资源类型，具体请参考微信开发手册
     */
    public function getLibraryCount(){
        $url = "https://api.weixin.qq.com/cgi-bin/material/get_materialcount?access_token=" . $this->getAccessToken();
        $data = self::curl($url);
        return json_decode($data, true);
    }

    /**
     * 获取永久素材 -- 梁子亮 2015年8月1日17:38:13 -- 出现一堆乱码，暂不知成功与否
     */
    public function getForverLibraryDetails($media_id=''){
        $url = "https://api.weixin.qq.com/cgi-bin/material/get_material?access_token=" . $this->getAccessToken();
        $arr = array(
            'media_id' => $media_id
            );
        $newdata = json_encode($arr);
        $data = self::http_post($url,$newdata);
        var_dump($data);die;
        return json_decode($data, true);
    }

    /**
     * 删除永久素材 -- 梁子亮 2015年8月1日22:04:57
     */
    public function delForverLibrary($media_id=''){
        $url = "https://api.weixin.qq.com/cgi-bin/material/del_material?access_token=" . $this->getAccessToken();
        $arr = array(
            'media_id' => $media_id
            );
        $newdata = json_encode($arr);
        $data = self::curlPost($url,$newdata);
        return true;
        // return json_decode($data, true);
    }

    /**
     * 增加永久图文素材 -- 梁子亮 2015年8月2日02:29:55
     */
    public function addForverWordImgDetails($data='')
    {
        $url = "https://api.weixin.qq.com/cgi-bin/material/add_news?access_token=" . $this->getAccessToken();
        $newdata = self::json_encode($data);
        $data = self::curlPost($url,$newdata);
        return json_decode($data, true);
        // var_dump($data);die;
    }

    /**
     * 增加永久图文素材 -- 梁子亮 2015年8月2日02:29:55
     */
    public function addForverWordImgDetails2($data='')
    {
        $url = "https://api.weixin.qq.com/cgi-bin/material/add_news?access_token=" . $this->getAccessToken();
        $data = self::curlPost($url,$data);
        return json_decode($data, true);
        // var_dump($data);die;
    }

}